-
Notifications
You must be signed in to change notification settings - Fork 0
50. Pow(x, n) #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
50. Pow(x, n) #47
Conversation
| 思考ログ: | ||
| - 再帰と同等の処理をloopに変換するのに時間がかかってしまった。。 | ||
| - https://github.com/hroc135/leetcode/pull/43/files#r2002294824 | ||
| - 思考のプロセスにどこか不自然な部分があるのだろうか。。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これ、うーん、素直かといわれると、ループを回している途中の引き継ぎの状態を考えてみましょう。
n = a * 2^k + b で、bit_mask = 2^(k-1) なんですよね。
x^a = powered_x
で、残りのタスクは
x^n = powered_x ^(2^k) * x^b = (x^a)^(2^k) * x^b = x^(a * 2^k + b)
ですよね。
これ、見た瞬間に読み取れますか。
ちなみに、再帰とは対応していませんね。
再帰は下の桁からかけています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
読み取れないですね。。
ただ、どう書くと素直に変換できるのか、うまく自分の中で整理できませんでした。
ちなみに下から掛けてる認識でしたが、違ってますでしょうか。
例えばx^6を求めるとして、再帰だと、
myPow(6) = myPow(3)^2 = (x * myPow(1)^2)^2 = (x * (x * myPow(0))^2)^2 = (x * (x * 1)^2)^2
こんなイメージで、ループでも、
(x * 1) -> (x * 1)^2 -> (x * (x * 1)^2)^2
と計算できている認識でした。
ただ、余分にxを描ける必要があるかどうか(例では1回目と2回目のループが必要)はnの上位ビットからみていく必要があるので、bit_mask = 1 << n.bit_length() - 1から確認していってます。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
すみません、再帰を self.myPow(x*x, n // 2) と勝手に思っていました。
ビットシフトするたびに2乗するというのは、速いでしょうからライブラリーの実装などどういう状況で呼ばれるか分からないものには適していると思います。一方で、枝の実装で、読む方を優先したいならば、少しアルゴリズムを変えてやりかたがあるかもしれません。
result = 1
current_bit = 1 << (n.bit_length() - 1)
current_n = 0
while current_n < n:
result *= result
current_n *= 2
if n & current_bit:
result *= x
current_n += 1
current_bit >>= 1
return resultが、まあ、しかし、「上のビットから回している」「全体を自乗すると指数が倍になる」がどちらにしても分かりにくいと思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
私の感覚はこういう方が素直です。n x を破壊していないし、base bit の関係がはっきりしているからですね。
result = 1
bit = 1
base = x
while bit <= n:
if n & bit:
result *= base
base *= base
bit <<= 1 There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。
意識してみます。
| powered_x *= x | ||
|
|
||
| x *= x | ||
| n >>= 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
こっちは、n を破壊しているために関係が見にくくなっているのだろうと思います。こちらのほうがまだましですが、この引き継ぎの瞬間、powered_x, x, n と答え(あと引数の元々の x と n)の関係はどうなっていますか。
答え = powered_x * x^n ですか。で、2^s <= (元々の n) / n となる最大の s とすると x = (元々の x) ^ (2^s) ですか。
元々の n, x を破壊しているのでややこしいことが起きています。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
はい、、ループに苦戦してずっと考えていたので、自分の中に色々前提が出来上がってしまっていて、逆にこれが自然に見えます。
ただ、しばらく経って読み返すと、よく分からなくなっている可能性が高い気がします。
| while bit_mask: | ||
| powered_x *= powered_x | ||
| if n & bit_mask: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bit_maskを用いず、n だけ使って1ビットずつずらしながら、1の位にビットがあるかどうかを見ていくのでも良いかもと感じました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
あ、下でやってますね。
| return x * self.myPow(x ** 2, n // 2) | ||
| else: | ||
| return self.myPow(x ** 2, n // 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
好みですが、self.myPow全体を2乗するのでも良いかもです。
| return self.myPow(1 / x, -n) | ||
|
|
||
| if n % 2 == 0: | ||
| return self.myPow(x, n // 2) * self.myPow(x, n // 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 回同じ関数を同じ引数で呼び出すより、 return self.myPow(x, n // 2) ** 2 としたほうが分かりやすいと感じました。
https://leetcode.com/problems/powx-n/description/